package view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JLabel;
import javax.swing.JPanel;

import model.Ammo;
import model.DefensiveItem;
import model.Explosion;
import model.GameDriver;
import model.Item;
import model.Map;
import model.Mine;
import model.Tile;
import model.Zombosaur;

import common.DataManager;
import common.GridCoordinate;

/**
 * @author Bill
 *
 *The map panel has three layers of jPanels. It has a lower 
 *grid panel which displays the background and trail, a transparent grid panel
 *over that panel, which is used to add items to the map, and then another panel which
 *is overlayed over that, which is not in grid format. The top layer is used to display the 
 *objects which are not organized in a grid, such as ammo.
 */
public class MapPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	private static int MAP_WIDTH = 15;
	private static int MAP_HEIGHT = 10;
	private static int TILE_WIDTH = 50;
	private static int TILE_HEIGHT = 50;
	
	private JPanel mapPanel = new JPanel(new GridLayout(MAP_HEIGHT,MAP_WIDTH));
	private JPanel mapGridOverlay = new JPanel(new GridLayout(MAP_HEIGHT,MAP_WIDTH));
	private JPanel mapOverlay = new JPanel(null){
		@Override
		public void paintComponent(Graphics g){
			super.paintComponent(g);
			Point rectCoordinate = gridCoordinateToPixels(new GridCoordinate(1,0));
			Color c = g.getColor();
			g.setColor(new Color(0.2f,0.2f,0.2f,0.7f));
			g.fillRoundRect((int)rectCoordinate.getX(), (int)rectCoordinate.getY()+TILE_HEIGHT/4, maxHealthWidth, TILE_HEIGHT/2, 5, 10);
			float red = 1 - ((float)healthWidth/(float)maxHealthWidth);
			float green = (float)healthWidth/(float)maxHealthWidth;
			if(red < 0 || green < 0){	//must ensure these don't go negative
				red = 1.0f;
				green = 0.0f;
			}
	    	g.setColor(new Color(red,green,0.0f,0.8f));
	    	g.fillRoundRect((int)rectCoordinate.getX(), (int)rectCoordinate.getY()+TILE_HEIGHT/4, healthWidth, TILE_HEIGHT/2, 5, 10);
	    	g.setColor(c);
		}
	};
	private static JPanel messagePanel = new JPanel(new BorderLayout());
	static JLabel messageLabel = new JLabel("",JLabel.CENTER);
	private GridCoordinate targetTile = new GridCoordinate();
	private SuicideButton suicideButton;
	private PauseButton pauseButton;	
	
	private int level = 1;
	private float healthWidthRatio = 0.5f;	//100 health corresponds to 200 pixels width
	private int maxHealthWidth = 200;
	private int healthWidth = maxHealthWidth;
	
	/**
	 * Constructs the map panel
	 * 
	 * @param trailList
	 */
	public MapPanel(ArrayList<GridCoordinate> trailList, int level){
		
		super();
		
		this.setLayout(null);
		this.setBackground(Color.black);
		this.level = level;

		mapPanel = new JPanel(new GridLayout(MAP_HEIGHT,MAP_WIDTH));
		mapPanel.setBackground(Color.black);
		mapPanel.setForeground(new Color(0.0f,0.5f,0.5f));
		mapPanel.setOpaque(false);
		for(int i = 0; i < MAP_WIDTH*MAP_HEIGHT; i++) {
			GridPanel image;
			GridCoordinate c = new GridCoordinate(i%MAP_WIDTH, i/MAP_WIDTH);
			if(searchTrailList(c)){
				image = new GridPanel(DataManager.getTrailTileName(level), c);
			} 
			else {
				image = new GridPanel(DataManager.getBackgroundTileName(level), c);
			}
			mapPanel.add(image);
		}
		mapPanel.setSize(mapPanel.getPreferredSize());

		mapGridOverlay = new JPanel(new GridLayout(MAP_HEIGHT,MAP_WIDTH));
		mapGridOverlay.setOpaque(false);
		for(int i = 0; i < MAP_WIDTH*MAP_HEIGHT; i++) {
			JLabel blankLabel = new JLabel("");
			blankLabel.setPreferredSize(new Dimension(TILE_WIDTH,TILE_HEIGHT));
			mapGridOverlay.add(blankLabel);	//add a blank JLabel so nothing is shown
		}
		mapGridOverlay.setSize(mapGridOverlay.getPreferredSize());
		
		mapOverlay.setOpaque(false);
		mapOverlay.setSize(mapGridOverlay.getPreferredSize());
	
		//place the spaceship
		//get the coordinate of the last tile in the trail to know where to put the spaceship
		GridCoordinate lastTrailTile = DataManager.getTrail(1).get(DataManager.getTrail(1).size()-1).getGridCoordinates();
		addItemToOverlay(new GridCoordinate(lastTrailTile.getX()+1,lastTrailTile.getY()-2), DataManager.SPACESHIP_IMAGE,0);
		addItemToOverlay(new GridCoordinate(0,0),DataManager.HEALTH_ICON,0);
		suicideButton = new SuicideButton();
		pauseButton = new PauseButton();
		addItemToOverlay(new GridCoordinate(this.MAP_WIDTH-1, this.MAP_HEIGHT-1), suicideButton,0);
		addItemToOverlay(new GridCoordinate(this.MAP_WIDTH-1, 0), pauseButton ,0);
		
		messageLabel.setForeground(Color.WHITE);
		messageLabel.setSize(new Dimension(200,100));
		messageLabel.setBackground(new Color(0.23f,0.13f,0.06f,0.4f));
		messageLabel.setOpaque(true);
		messagePanel.add(messageLabel, BorderLayout.CENTER);
		messagePanel.setOpaque(false);
		messagePanel.setSize(mapGridOverlay.getPreferredSize());
		messagePanel.setVisible(false);
		
		this.setPreferredSize(mapPanel.getPreferredSize());
		this.setSize(mapPanel.getPreferredSize());
		this.add(messagePanel);
		this.add(mapOverlay);
		this.add(mapGridOverlay);
		this.add(mapPanel);
	}
	
	/**
	 * Adds an object to the transparent grid, at the coordinate specified.
	 * 
	 * @param coord
	 * @param image
	 */
	public void addItemToGrid(GridCoordinate coord, ImagePanel image, int rotation){
		int coordIndex = coord.getY()*MAP_WIDTH + coord.getX();
		this.mapGridOverlay.remove(coordIndex);
		image.setOpaque(false);
		image.setRotation(rotation);
		this.mapGridOverlay.add(image,coordIndex);
		this.revalidate();
	}
	
	/**
	 * Adds an object to the transparent grid, at the coordinate specified.
	 * 
	 * @param coord
	 * @param fileName
	 */
	public void addItemToGrid(GridCoordinate coord, String fileName, int rotation){
		addItemToGrid(coord, new ImagePanel(fileName), rotation);
		//addItemToGrid(coord, new ImagePanel(ImageFactory.getInstance().getPzombiedactylImage()));
	}
	
	/**
	 * removes an item from the map grid overlay
	 * 
	 * @param int index
	 */
	public void removeItemFromGrid(int index){
		int y = index/Map.getMapWidth();
		int x = index%Map.getMapWidth();
		Tile tile = (Map.getMapGrid().get(y)).get(x);
		if(tile.hasItem()){
		this.mapGridOverlay.remove(index);
		JLabel blankLabel = new JLabel("");
		blankLabel.setPreferredSize(new Dimension(TILE_WIDTH,TILE_HEIGHT));
		mapGridOverlay.add(blankLabel, index);	//add a blank JLabel so nothing is shown
		this.revalidate();
		}
	}
	
	/**
	 * Adds an object to the transparent panel over the grid panels, but at a specific
	 * grid coordinate.
	 * 
	 * @param coord
	 * @param fileName
	 */
	public void addItemToOverlay(GridCoordinate coord, String fileName, int rotation){
		ImagePanel image = new ImagePanel(fileName);
		//ImagePanel image = new ImagePanel(ImageFactory.getInstance().getPzombiedactylImage());
		image.setOpaque(false);
		image.setRotation(rotation);
		image.setSize(image.getPreferredSize());
		this.mapOverlay.add(image);
		image.setLocation(gridCoordinateToPixels(coord));
		this.revalidate();
	}
	
	/**
	 * Adds an object to the transparent panel over the grid panels given it's image panel, but at a specific
	 * grid coordinate.
	 * 
	 * @param coord
	 * @param fileName
	 */
	public void addItemToOverlay(GridCoordinate coord, ImagePanel image, int rotation){
		image.setOpaque(false);
		image.setRotation(rotation);
		image.setSize(image.getPreferredSize());
		this.mapOverlay.add(image);
		image.setLocation(gridCoordinateToPixels(coord));
		this.revalidate();
	}
	
	/**
	 * Adds an object to the transparent panel over the grid panels at a specific x and y coordinate
	 * 
	 * @param x, y
	 * @param fileName
	 */
	public void addItemToOverlay(float x, float y, String fileName, int rotation){
		ImagePanel image = new ImagePanel(fileName);
		//ImagePanel image = new ImagePanel(ImageFactory.getInstance().getPzombiedactylImage());
		image.setOpaque(false);
		image.setRotation(rotation);
		image.setSize(image.getPreferredSize());
		this.mapOverlay.add(image);
		image.setLocation(gridCoordinatesToPixels(x,y));
		this.revalidate();
	}
	
	/**
	 * Clears all the items on the map overlay except for the spaceship and health info
	 */
	public void clearItemsOnMap(){
		//start at 4 so we dont lose the spaceship and health and pause and suicide button
		for(int i = 4; i < mapOverlay.getComponentCount(); i++){
			mapOverlay.remove(i);
		}
		for(int i = 0; i < mapGridOverlay.getComponentCount(); i++){
			removeItemFromGrid(i);
		}
		
	}
	
	/**
	 * Hides all the items on the map overlay except for the spaceship and health info
	 */
	public void hideItemsOnMap(){
		//start at 4 so we dont lose the spaceship and health and pause and suicide button
		for(int i = 4; i < mapOverlay.getComponentCount(); i++){
			mapOverlay.getComponent(i).setVisible(false);
		}
		for(int i = 0; i < mapGridOverlay.getComponentCount(); i++){
			mapGridOverlay.getComponent(i).setVisible(false);
		}
	}
	
	private int updateCount = 0;
	public void tickUpdate(GameDriver gameDriver){
		if(updateCount == 30){
			clearItemsOnMap();
			updateCount = 0;
		}
		else {
			hideItemsOnMap();
			updateCount++;
		}
		addItemToOverlay(targetTile,DataManager.TILE_COVER,0);
		for(int i = 0; i < MAP_WIDTH*MAP_HEIGHT; i++){
			int y = i/Map.getMapWidth();
			int x = i%Map.getMapWidth();
			Tile tile = (Map.getMapGrid().get(y)).get(x);
			if(tile.hasItem()){
				Item item = ((Map.getMapGrid().get(y)).get(x)).getItem();
				addItemToGrid(item.getPosition(), item.getImageName(), item.getRotation()+90);
			}
		}
		ArrayList<Zombosaur> zombosOnMap = gameDriver.getMap().getZombosaursOnMap();
		for(int i = 0; i < zombosOnMap.size(); i++){
			Zombosaur z = zombosOnMap.get(i);
			addItemToOverlay(z.getxPos()-0.5f, z.getyPos()-0.5f, z.getImageName(), z.getDirection()+90);
		}
		List<Ammo> ammoOnMap = gameDriver.getMap().getAmmoOnMap();
		for(int i = 0; i < ammoOnMap.size(); ++i){
			Ammo a = ammoOnMap.get(i);
			addItemToOverlay(a.getX(), a.getY(), a.getWeapon().getAmmoImageName(), 0);
		}
		List<Ammo> autoAmmoOnMap = gameDriver.getMap().getAutoAmmoOnMap();
		for(int i = 0; i < autoAmmoOnMap.size(); ++i){
			Ammo a = autoAmmoOnMap.get(i);
			addItemToOverlay(a.getX(), a.getY(), a.getWeapon().getAmmoImageName(), 0);
		}
		ArrayList<Explosion> explosions = gameDriver.getMap().getExplosionsOnMap();
		for(int i = 0; i < explosions.size(); i++){
			addItemToOverlay(explosions.get(i).getPosition(), explosions.get(i).getImage(), 0);
		}
		this.maxHealthWidth = (int)(gameDriver.getPlayer().getMaxHealth()/healthWidthRatio);
		this.healthWidth = (int)(gameDriver.getPlayer().getHealth()*this.maxHealthWidth/gameDriver.getPlayer().getMaxHealth());
	}
	
	public void nonTickUpdate(GameDriver gameDriver){
		for(int i = 0; i < gameDriver.getMap().getAutomaticWeaponCount(); i++){
			addItemToGrid(gameDriver.getMap().getWeaponsOnMap().get(i).getPosition(), gameDriver.getMap().getWeaponsOnMap().get(i).getImageName(), gameDriver.getMap().getWeaponsOnMap().get(i).getRotation()+90);
		}
		List<DefensiveItem> defensiveItems = gameDriver.getMap().getDefensiveItemOnMap();
		for(int i = 0; i <defensiveItems.size(); i++){
			DefensiveItem item = defensiveItems.get(i);
			addItemToGrid(item.getPosition(), item.getImageName(), item.getRotation()+90);
		}
		for(int i = 0; i < gameDriver.getMap().getMinesOnMap().size(); i++){
			Mine mine = gameDriver.getMap().getMinesOnMap().get(i);
			addItemToGrid(mine.getPosition(), mine.getImageName(), 0);	//rotation is 0 because mines are circular
		}
		this.maxHealthWidth = (int)(gameDriver.getPlayer().getMaxHealth()/healthWidthRatio);
		this.healthWidth = (int)(gameDriver.getPlayer().getHealth()*this.maxHealthWidth/gameDriver.getPlayer().getMaxHealth());
	}
	 
	/**
	 * Converts a map coordinate to a view coordinate. (World to view)
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
    private Point gridCoordinatesToPixels(float x, float y){
    	int pX = (int) (x*TILE_WIDTH);
    	int pY = (int) (y*TILE_HEIGHT);
    	return new Point(pX,pY);
    }
    
    /**
     * returns a point which is shifted back and up by hald a grid coordinate.
     * This is for items which need to be drawn to the screen, but who's coordinate
     */
	
	/**
	 * Converts a view coordinate to a map coordinate. (View to World)
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
    private GridCoordinate pixelsToGridCoordinates(int x, int y){
    	int gridX = x/TILE_WIDTH;
    	int gridY = y/TILE_HEIGHT;
    	return new GridCoordinate(gridX,gridY);
    }
    
    /**
     * Converts a grid coordinate to a point in the view. (World to View)
     * 
     * @param g
     * @return
     */
    private Point gridCoordinateToPixels(GridCoordinate g){
    	int pX = g.getX()*TILE_WIDTH;
    	int pY = g.getY()*TILE_HEIGHT;
    	return new Point(pX,pY);
    }
    
    /**
     * Returns true if the tile at "coordinate" is a trail tile.
     * 
     * @param coordinate
     * @return
     */
    private boolean searchTrailList(GridCoordinate coordinate){
    	for(int i = 0; i < DataManager.getTrail(this.level).size(); i++){
    		if(DataManager.getTrail(this.level).get(i).getGridCoordinates().getX() == coordinate.getX() && DataManager.getTrail(this.level).get(i).getGridCoordinates().getY() == coordinate.getY()) {
    			return true;
    		}
    	}
    	return false;
    }
    
    /**
     * displays a message on the message panel
     */
    public void displayMessage(String message){
    	messageLabel.setText(message);
    	messagePanel.setVisible(true);
    }
    
    /**
     * removes the message shown on the message panel
     */
    public void removeMessage(){
    	messagePanel.setVisible(false);
    	messageLabel.setText("");
    }
    
    public GridCoordinate getSelectedTile() {
		return targetTile;
	}

	public void setSelectedTile(GridCoordinate selectedTile) {
		this.targetTile = selectedTile;
	}


}